package br.com.j7.jdesk.library.util;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class ClassUtils {

	public static final String CGLIB_CLASS_SEPARATOR = "$$";
	
	public static List<String> listClassesFromPackage(String...packagesNames) throws ClassNotFoundException, IOException{
		List<String> list = new ArrayList<String>();
		ClassLocator classLoader = new ClassLocator(packagesNames);
		List<ClassLocation> classes = classLoader.getAllClassLocations();
		for(ClassLocation cl : classes){
			list.add(cl.getClassName());
		}
		return list;
	}
	
//	public static Method findMethodEventHandler(Object target,String eventName){
//		Class clazz = (isCglibProxy(target.getClass()))?target.getClass().getSuperclass():target.getClass();
//		Method[] methods = clazz.getMethods();
//		
//		for(Method m : methods){
//			if(m.isAnnotationPresent(WebEvent.class)){
//				WebEvent event = m.getAnnotation(WebEvent.class);
//				if(event.name().equalsIgnoreCase(eventName))
//					return m;
//			}
//		}
//		return null;
//	}
	
	public static Boolean isCglibProxy(Class clazz){
		return (clazz!=null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR));
	}
	
	public static Map<String,Method> loadMethods(Class targetClass,String regex,Integer numberOfParameters)throws Exception{
		HashMap<String, Method> map = new HashMap<String, Method>();
		Method[] methods = targetClass.getMethods();
		for(Method m : methods){
			if(numberOfParameters!=null){
				Class[] parameterTypes = m.getParameterTypes();
				if(parameterTypes.length!=numberOfParameters)
					continue;
			}
			if(regex==null || "".equals(regex)){
				map.put(m.getName(),m);
			}else if(StringUtils.find(m.getName(),regex)){
				map.put(m.getName(),m);
			}
		}
		return map;
	}
	
	public static Map<String,Method> loadSetsMethods(Class targetClass,Integer numberOfParameters)throws Exception{
		return loadMethods(targetClass, "^set",numberOfParameters);
	}
	
	public static Map<String,Method> loadSetsMethods(Class targetClass)throws Exception{
		return loadSetsMethods(targetClass, null);
	}
	
	public static Map<String,Method> loadGetsMethods(Class targetClass)throws Exception{
		return loadMethods(targetClass, "^get",null);
	}
	
	public static String buildMethodName(String prefix,String fieldName){
		return prefix + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
	}
	
	public static String buildSetMethodName(String fieldName){
		return buildMethodName("set",fieldName);
	}
	
	public static String findFieldName(List<Field> fields,String fieldName){
		for(Field f : fields){
			if(f.getName().equalsIgnoreCase(fieldName)){
				return f.getName();				
			}
		}
		return null;
	}
	
	public static String buildGetMethodName(String fieldName){
		return buildMethodName("get",fieldName);
	}
	
	public static Boolean isClassField(Object obj,String fieldName){
		try{
			obj.getClass().getDeclaredField(fieldName);
		}catch (Exception e) {
			return false;
		}
		return true;
	}
	
	public static Boolean isClassMethod(Object obj,String methodName){
		try{
			Map<String,Method> methods = ClassUtils.loadMethods(obj.getClass(),null,null);
			return methods.containsKey(methodName);
		}catch (Exception e) {
			return false;
		}
	}
	
	public static Boolean hasMethodGet(Object obj,Field field){
		try{
			obj.getClass().getMethod(buildGetMethodName(field.getName()));
			return true;
		}catch (Exception e) {
			return false;
		}
	}
	
	public static <T> T getValueFromResultSet(Object obj,String methodName, String parameterValue) throws Exception {
		Method m = obj.getClass().getMethod(methodName, String.class);
		return (T) m.invoke(obj, parameterValue);
	}
	
	public static <T> T getValueFromField(Object obj,Field field) throws Exception {
		String methodName = buildGetMethodName(field.getName());
		Method m = obj.getClass().getMethod(methodName);
		return (T) m.invoke(obj);
	}

	public static List<Field> loadFields(Class targetClass)throws Exception{
		List<Field> list = new ArrayList<Field>();
		Field[] fields = targetClass.getDeclaredFields();
		for(Field f : fields){
			list.add(f);
		}
		if(targetClass.getSuperclass()!=null){
			list.addAll(loadFields(targetClass.getSuperclass()));
		}
		return list;
	}
	
	public static List<String> loadFieldsName(Class targetClass)throws Exception{
		List<String> list = new ArrayList<String>();
		List<Field> fields = ClassUtils.loadFields(targetClass);
		for(Field f : fields){
			list.add(f.getName());
		}
		return list;
	}
	
	public static String formatMethodName(String methodName){
		if("getInteger".equals(methodName) || "setInteger".equals(methodName))
			return methodName.substring(0,6);
		return methodName; 
	}
	
	public static Class loadClass(String className){
		try{
			return Class.forName(className);
		}catch (Exception e) {
			return null;
		}
	}
	
//	public static <T extends Copyable> T clone(Copyable obj){
//		T target = null;
//		try{
//			target = (T)obj.getClass().newInstance();
//			target.copy(obj);
//		}catch (Exception e) {
//			e.printStackTrace();
//		} 
//		return target;		
//	}
	
	public static Object invokeGetMethod(Object target,String fieldName)throws Exception{
		Method method = target.getClass().getMethod(ClassUtils.buildGetMethodName(fieldName));
		return method.invoke(target);
	}

	public static void invokeSetMethod(Object target,Field field,Object value)throws Exception{
		value = ConverterUtils.getByFieldType(field, value);
		if(value!=null)
			invokeSetMethod(target, field.getName(), value);
	}
	
	public static void invokeSetMethod(Object target,String fieldName,Object value)throws Exception{
		Method method = target.getClass().getMethod(ClassUtils.buildSetMethodName(fieldName),value.getClass());
		method.invoke(target,value);
	}
	
	public static Object invokeArgsMethod(Object target,String methodName,Object...values)throws Exception{
		List<Class> listArgsClass = new ArrayList<Class>();
		for(Object obj : values){
			listArgsClass.add(obj.getClass());
		}
		Class[] argsClass = new Class[listArgsClass.size()]; 
		Method method = target.getClass().getMethod(methodName,listArgsClass.toArray(argsClass));
		return method.invoke(target,values);
	}
	
	public static Object invokeNoArgsMethod(Object target,String methodName)throws Exception{
		Map<String,Method> methods = loadMethods(target.getClass(),methodName,null);
		Method method = methods.get(methodName);
		return (method==null)?null:method.invoke(target);
	}
}